Skip to content

fix(egress): single composite fanning out to all destinations#20

Merged
ralyodio merged 1 commit into
masterfrom
fix/egress-single-composite
Jun 22, 2026
Merged

fix(egress): single composite fanning out to all destinations#20
ralyodio merged 1 commit into
masterfrom
fix/egress-single-composite

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Problem

Streaming to Twitch + YouTube at once: Twitch works, YouTube hangs on "Preparing stream", and the host intermittently gets "could not establish pc connection".

Root cause is a single CPU ceiling. The previous commit (b3860a1) started one isolated egress per RTMP destination, which duplicates the entire headless-Chrome render + H264 encode pipeline per platform. Two platforms = ~2× CPU; the 4-vCPU SFU droplet thrashed (load ~9):

  • The second composite (YouTube) starved → stuck on "Preparing".
  • The startup CPU spike delayed the host publisher's DTLS handshake server-side → surfaced to the client as "could not establish pc connection" (force-relay/TURN was already working; this was the server, not the laptop).

Confirmed live in the SFU logs: load average 9.16 on 4 cores, four StartRoomCompositeEgress (Chrome) participants across overlapping sessions; publisher hit dtls timeout then reconnected via TURN.

Fix

Collapse back to a single RoomComposite carrying every URL in one StreamOutput. One render + one encode regardless of platform count; each extra URL is just a cheap mux. ~2.0 CPU — fits 4 vCPU comfortably. keyFrameInterval:1 (unchanged) already mitigates the YouTube-"Preparing" back-pressure that motivated the per-destination split.

Only start/route.ts changed; stop route and desktop already flatten egressIds[], so they're compatible with the single-element array.

Trade-off

With one shared composite, a hard stall on one platform can back-pressure the other — the thing the split prevented. In practice keyFrameInterval:1 already fixed the actual trigger (YouTube "Preparing"). If both platforms and hard isolation are needed later, that's the case for an 8-vCPU (ideally dedicated-CPU) droplet.

Test

route.test.ts updated to assert a single startRoomCompositeEgress call carrying both URLs. 4/4 pass.

🤖 Generated with Claude Code

One isolated egress per RTMP destination (b3860a1) duplicated the whole
headless-Chrome render + H264 encode per platform, so Twitch + YouTube
needed ~2x CPU. The 4-vCPU SFU droplet thrashed (load ~9): the second
composite (YouTube) starved and hung on "Preparing", and the startup CPU
spike delayed the host publisher's DTLS handshake server-side, surfacing
as "could not establish pc connection".

Collapse back to a single RoomComposite carrying every URL in one
StreamOutput: one render + one encode regardless of platform count, each
extra URL just a cheap mux. ~2.0 CPU, fits 4 vCPU. keyFrameInterval:1
already mitigates the YouTube-"Preparing" back-pressure the split targeted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio ralyodio merged commit 169551f into master Jun 22, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant